package com.fast.develop.api;

import com.fast.develop.core.enums.DdlAuto;
import com.fast.develop.api.rest.registor.*;
import com.fast.develop.api.rest.FileRest;
import com.fast.develop.api.rest.RuntimeRest;
import com.fast.develop.api.rest.SchemaRest;
import com.fast.develop.core.ModuleContextFactory;
import com.fast.develop.core.io.ResourceManager;
import com.fast.develop.core.plugin.PluginRegister;
import com.fast.develop.core.properties.DomainRuntimeProperties;
import com.fast.develop.core.registry.ModuleRegistry;
import com.fast.develop.core.web.MappingRegistor;
import com.fast.develop.core.web.MultiMappingRegistor;
import com.fast.develop.integration.HibernateModuleConfiguration;
import com.fast.develop.integration.HibernateModuleConfigurationBuilder;
import com.fast.develop.integration.context.HibernateDomainModuleContextFactory;
import com.fast.develop.integration.web.MvcMappingHandlerMapping;
import com.fast.develop.management.ManagementConfiguration;
import com.fast.develop.meta.base.MetaConfiguration;
import com.fast.develop.meta.base.MetaRegistor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.sql.DataSource;
import java.util.*;

/**
 * @author 莫寅
 * @createTime 2022-06-18 03:27
 * @desc：
 */
@Slf4j
public class FastDevelopConfiguration implements InitializingBean {


    private DataSource dataSource;

    private MvcMappingHandlerMapping mvcMappingHandlerMapping;

    private List<MetaRegistor> metaRegistors = new ArrayList<>();

    private ModuleContextFactory moduleContextFactory;

    private DomainRuntimeProperties domainProperties = new DomainRuntimeProperties();

    private String[] packagesToScan;

    private Class[] annotatedClasses;

    private String[] mappingResources;

    private boolean showSql = false;

    private DdlAuto ddlAuto;

    private MappingRegistor mappingRegistor;

    private ManagementConfiguration managementConfiguration;


    private MetaConfiguration metaConfiguration;

    private ResourceManager resourceManager;

    /**
     * 请求拦截器
     */
    private final ObjectProvider<List<PluginRegister>> luginRegisterProvider;

    public FastDevelopConfiguration(
            ResourceManager resourceManager,
            DataSource dataSource,
            RequestMappingHandlerMapping requestMappingHandlerMapping,
            ObjectProvider<List<PluginRegister>> luginRegisterProvider
    ) {
        this.luginRegisterProvider = luginRegisterProvider;
        this.mvcMappingHandlerMapping = new MvcMappingHandlerMapping(requestMappingHandlerMapping);
        this.dataSource = dataSource;
        this.resourceManager = resourceManager;
    }


    private void initMultiMappingRegistor(){
        FileRest fileRest = new FileRest(managementConfiguration);
        RuntimeRest runtimeRest = new RuntimeRest(this, moduleContextFactory);
        SchemaRest schemaRest = new SchemaRest(managementConfiguration);
        MultiMappingRegistor multiMappingRegistor = new MultiMappingRegistor();
        multiMappingRegistor.register(new FileMappingRegistor(mvcMappingHandlerMapping, domainProperties, fileRest));
        multiMappingRegistor.register(new RuntimeMappingRegistor(mvcMappingHandlerMapping, domainProperties, runtimeRest));
        multiMappingRegistor.register(new SchemaMappingRegistor(mvcMappingHandlerMapping, domainProperties, schemaRest));

        this.mappingRegistor = multiMappingRegistor;
    }


    public ModuleRegistry getModuleRegistry(){
        ModuleRegistry moduleRegistry = new ModuleRegistry();
        List<PluginRegister> registers = luginRegisterProvider.getIfAvailable(Collections::emptyList);
        registers.forEach(register -> {
            moduleRegistry.register(register);
        });
        return moduleRegistry;
    }


    public void reload(){
        long start = System.currentTimeMillis();
        moduleContextFactory.destroy();
        managementConfiguration.reload();
        createModuleContextFactory(moduleContextFactory);


        System.out.println(System.currentTimeMillis() - start);
    }

    private ModuleContextFactory moduleContextFactory(){
        this.metaConfiguration = new MetaConfiguration().register(metaRegistors.toArray(new MetaRegistor[]{}));
        metaConfiguration.build();
        this.managementConfiguration = new ManagementConfiguration(resourceManager, metaConfiguration);


        ModuleContextFactory moduleContextFactory = new HibernateDomainModuleContextFactory(
                managementConfiguration.getMetaConfiguration(),
                getModuleRegistry(),
                mvcMappingHandlerMapping
        );

        //生成模块上下文工厂
        this.moduleContextFactory = createModuleContextFactory(moduleContextFactory);

        return moduleContextFactory;
    }


    /**
     * 创建模块上下文工厂
     * 生成模块默认上下文，并加载领域模型
     * @return
     */
    private ModuleContextFactory createModuleContextFactory(ModuleContextFactory moduleContextFactory){
        Map properties = new HashMap();
        properties.put("hibernate.show_sql", showSql);
        if(ddlAuto != null){
            properties.put("hibernate.hbm2ddl.auto", ddlAuto.getValue());
        }

        HibernateModuleConfiguration hibernateModuleConfiguration = new HibernateModuleConfigurationBuilder()
                .dataSource(dataSource)
                .mappingResources(mappingResources)
                .packagesToScan(packagesToScan)
                .annotatedClasses(annotatedClasses)
                .properties(properties)
                .enableResourcesRest()
                .domainRuntimeProperties(domainProperties)
                .metaConfiguration(metaConfiguration)
//                .registerMetaType(metaRegistors.toArray(new MetaRegistor[]{}))
                .build();

        moduleContextFactory.create(hibernateModuleConfiguration);

        this.metaConfiguration = hibernateModuleConfiguration.getMetaConfiguration();
        return moduleContextFactory;
    }

    public ModuleContextFactory getModuleContextFactory() {
        return moduleContextFactory;
    }

    public FastDevelopConfiguration domainProperties(DomainRuntimeProperties domainProperties) {
        this.domainProperties = domainProperties;
        return this;
    }

    public FastDevelopConfiguration packagesToScan(String[] packagesToScan) {
        this.packagesToScan = packagesToScan;
        return this;
    }

    public FastDevelopConfiguration addMetaRegistor(MetaRegistor... metaRegistorArray) {
        Arrays.stream(metaRegistorArray).forEach(metaRegistor -> metaRegistors.add(metaRegistor));
        return this;
    }

    public FastDevelopConfiguration annotatedClasses(Class[] annotatedClasses) {
        this.annotatedClasses = annotatedClasses;
        return this;
    }

    public FastDevelopConfiguration mappingResources(String[] mappingResources) {
        this.mappingResources = mappingResources;
        return this;
    }

    public FastDevelopConfiguration showSql(boolean showSql) {
        this.showSql = showSql;
        return this;
    }

    public FastDevelopConfiguration ddlAuto(DdlAuto ddlAuto) {
        this.ddlAuto = ddlAuto;
        return this;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //生成模块 -》 领域模型
        moduleContextFactory();
        //初始化映射注册器
        initMultiMappingRegistor();
        //注册映射
        mappingRegistor.registerMapping();
    }

    public ManagementConfiguration getManagementConfiguration() {
        return managementConfiguration;
    }

    public MvcMappingHandlerMapping getMvcMappingHandlerMapping() {
        return mvcMappingHandlerMapping;
    }
}